// SPDX-License-Identifier: GPL-3.0-or-later

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtMultimedia
import Qt5Compat.GraphicalEffects

import Fk
import Fk.Components.Common
import Fk.Widgets as W
import "RoomLogic.js" as Logic
import "components" as QsgsComponents
import "components/dashboard"
import "components/buttons" as QsgsButton
import "components/room" as QsgsRoom
import "self" as QsgsSelf

W.PageBase {
  id: roomScene

  property int playerNum: 0

  property alias popupBox: popupBox
  property alias manualBox: manualBox
  property alias bigAnim: bigAnim
  property alias promptText: prompt.text
  property var currentPrompt
  property alias okCancel: okCancel
  property alias okButton: okButton
  property alias cancelButton: cancelButton
  property alias dynamicCardArea: dynamicCardArea
  property alias tableCards: tablePile.cards
  property alias dashboard: dashboard
  property int dashboardId: 0
  property int changingId: 0
  property alias drawPile: drawPile
  property alias skillInteraction: skillInteraction
  property alias miscStatus: miscStatus
  property alias banner: banner

  // 权宜之计 后面全改
  property alias cheatDrawer: cheatLoader

  property var selected_targets: []
  property string responding_card
  property var extra_data: ({})
  property var skippedUseEventId: []

  QtObject {
    id: selfPhoto
    property var id // 绑定的话就不能changeself了.. : Self.id
    property var index: 0   // For animating seat swap
    property var general: Self.avatar
    property var avatar: Self.avatar
    property var deputyGeneral: ""
    property var screenName: Self.screenName
    property var role: "unknown"
    property var role_shown: false
    property var kingdom: "unknown"
    property var netstate: "online"
    property var maxHp: 0
    property var hp: 0
    property var shield: 0
    property var seatNumber: 1
    property var dead: false
    property var dying: false
    property var faceup: true
    property var chained: false
    property var drank: 0
    property var rest: 0
    property var surrendered: false
    property var sealedSlots: "[]"
  }

  MediaPlayer {
    id: bgm
    source: Config.bgmFile

    loops: MediaPlayer.Infinite
    onPlaybackStateChanged: {
      if (playbackState == MediaPlayer.StoppedState)
        play();
    }
    audioOutput: AudioOutput {
      volume: Config.bgmVolume / 100
    }
  }

  states: [
    State { name: "notactive" },
    State { name: "active" }
  ]

  state: "notactive"
  transitions: [
    Transition {
      from: "*"; to: "notactive"
      ScriptAction {
        script: {
          skillInteraction.sourceComponent = undefined;
          promptText = "";
          okCancel.visible = false;
          okButton.enabled = false;
          cancelButton.enabled = false;
          endPhaseButton.visible = false;
          progress.visible = false;
          extra_data = {};

          dashboard.disableAllCards();
          dashboard.disableSkills();
          dashboard.pending_skill = "";
          // dashboard.retractAllPiles();

          selfEquipAreaItem.equipArea.disableSkills();

          for (let i = 0; i < photoModel.count; i++) {
            const item = photos.itemAt(i);
            item.state = "normal";
            item.selected = false;
            // item.selectable = false;
          }
          selfPhotoItem.state = "normal";
          selfDeputyPhotoItem.state = "normal";
          selfPhotoItem.selected = "normal";
          selfDeputyPhotoItem.selected = "normal";

          if (popupBox.item != null) {
            popupBox.item.finished();
          }

          Lua.call("FinishRequestUI");
          applyChange({});
        }
      }
    },

    Transition {
      from: "notactive"; to: "active"
      ScriptAction {
        script: {
          const dat = Backend.getRequestData();
          const total = dat["timeout"] * 1000;
          const now = Date.now(); // ms
          const elapsed = now - (dat["timestamp"] ?? now);

          if (total <= elapsed) {
            roomScene.state = "notactive";
          }

          progressAnim.from = (1 - elapsed / total) * 100.0;
          progressAnim.duration = total - elapsed;
          progress.visible = true;
        }
      }
    }
  ]

  /* Layout:
   * +---------------------+
   * |   Photos, get more  |
   * | in arrangePhotos()  |
   * |      tablePile      |
   * | progress,prompt,btn |
   * +---------------------+
   * |      dashboard      |
   * +---------------------+
   */

  ListModel {
    id: photoModel
  }

  //roomArea 公共区域
  Item {
    id: roomArea
    width: roomScene.width - chatArea.width
    height: roomScene.height - selfArea.height

    Repeater {
      id: photos
      model: photoModel
      QsgsComponents.Photo {
        playerid: model.id
        general: model.general
        avatar: model.avatar
        deputyGeneral: model.deputyGeneral
        screenName: model.screenName
        role: model.role
        role_shown: model.role_shown
        kingdom: model.kingdom
        netstate: model.netstate
        maxHp: model.maxHp
        hp: model.hp
        shield: model.shield
        seatNumber: model.seatNumber
        dead: model.dead
        dying: model.dying
        faceup: model.faceup
        chained: model.chained
        drank: model.drank
        rest: model.rest
        surrendered: model.surrendered
        sealedSlots: JSON.parse(model.sealedSlots)
        frameStyle: Logic.getLevelById(model.id)

        visible: index !== 0

        onSelectedChanged: {
          if ( state === "candidate" )
            Lua.call("UpdateRequestUI", "Photo", playerid, "click", { selected, autoTarget: Config.autoTarget } );
        }

        onDoubleTappedChanged: {
          if (doubleTapped && enabled) {
            // Lua.call("UpdateRequestUI", "Photo", playerid, "doubleClick", { selected, doubleClickUse: Config.doubleClickUse, autoTarget: Config.autoTarget } )
            doubleTapped = false;
          }
        }
      }
    }

    onWidthChanged: Logic.arrangePhotos();
    onHeightChanged: Logic.arrangePhotos();

    InvisibleCardArea {
      id: drawPile
      x: parent.width / 2
      y: roomScene.height / 2
    }

    TablePile {
      id: tablePile
      width: parent.width * 0.7
      height: 150
      x: parent.width * 0.15
      y: parent.height * 0.6 + 10
    }
  }

  //control 烧条与按钮
  Item {
    id: controls
    anchors.bottom: selfArea.top
    anchors.bottomMargin: 30
    width: roomScene.width

    property alias progressBar: progress 

    Text {
      id: prompt
      visible: progress.visible
      anchors.bottom: progress.top
      anchors.bottomMargin: 4
      z: 1
      color: '#ffffff'
      font.pixelSize: 14
      font.family: "SimSun"
      // style: Text.Outline
      // styleColor: "#3D2D1C"
      textFormat: TextEdit.RichText
      anchors.horizontalCenter: progress.horizontalCenter
    }

    //神秘组件（为什么textFormat没传给GlowText？）
    Glow {
      id: promptGlow
      source: prompt
      anchors.fill: prompt
      color: '#000000'
      spread: 0.9
      radius: 2.5
    }

    ProgressBar {
      id: progress
      width: roomArea.width * 0.5
      height: 8
      x: roomArea.width / 2 - width / 2
      from: 0.0
      to: 100.0

      visible: false

      background: Rectangle {
        implicitWidth: 200
        implicitHeight: 12
        color: "black"
        radius: 6
      }

      contentItem: Item {
        implicitWidth: 140
        implicitHeight: 8

        Rectangle {
          width: progress.visualPosition * parent.width
          height: parent.height
          radius: 6
          gradient: Gradient {
            GradientStop { position: 0.0; color: '#fff1f1' }
            GradientStop { position: 0.2; color: "#d40505" }
            GradientStop { position: 0.7; color: '#d40505' }
            GradientStop { position: 1.0; color: '#7c0404' }
          }
        }
      }

      NumberAnimation on value {
        id: progressAnim
        running: progress.visible
        from: 100.0
        to: 0.0
        duration: Config.roomTimeout * 1000

        onFinished: {
          roomScene.state = "notactive"
        }
      }
    }

    Rectangle {
      anchors.bottom: parent.bottom
      anchors.bottomMargin: 8
      anchors.right: okCancel.left
      anchors.rightMargin: 20
      color: "#88EEEEEE"
      radius: 8
      visible: {
        if (roomScene.state !== "active") {
          return false;
        }
        if (!specialCardSkills) {
          return false;
        }
        if (specialCardSkills.count > 1) {
          return true;
        }
        return (specialCardSkills.model ?? false)
            && specialCardSkills.model[0] !== "_normal_use"
      }
      width: childrenRect.width
      height: childrenRect.height - 20

      RowLayout {
        y: -10
        Repeater {
          id: specialCardSkills
          RadioButton {
            property string orig_text: modelData
            text: Lua.tr(modelData)
            checked: index === 0
            onCheckedChanged: {
              Lua.call("UpdateRequestUI", "SpecialSkills", "1", "click", modelData);
            }
          }
        }
      }
    }

    Loader {
      id: skillInteraction
      anchors.verticalCenter: progress.verticalCenter
      anchors.right: progress.left
      anchors.rightMargin: 10
    }

    Row {
      id: okCancel
      anchors.top: progress.bottom
      anchors.topMargin: 15
      anchors.horizontalCenter: progress.horizontalCenter
      spacing: 20
      visible: false

      QsgsComponents.OLChoiceButton {
        id: skipNullificationButton
        text: Lua.tr("SkipNullification")
        visible: !!roomScene.extra_data.useEventId
                 && !roomScene.skippedUseEventId.find(id => id === extra_data.useEventId)
        onClicked: {
          roomScene.skippedUseEventId.push(roomScene.extra_data.useEventId);
          Lua.call("UpdateRequestUI", "Button", "Cancel");
        }
      }

      QsgsComponents.OLChoiceButton {
        id: okButton
        enabled: false
        textFont.letterSpacing: 12
        text: Lua.tr("OK")
        glow.color: '#300d04'
        onClicked: Lua.call("UpdateRequestUI", "Button", "OK");
      }

      QsgsComponents.OLChoiceButton {
        id: cancelButton
        enabled: false
        text: Lua.tr("Cancel")
        textFont.letterSpacing: 12
        glow.color: '#300d04'
        onClicked: Lua.call("UpdateRequestUI", "Button", "Cancel");
      }

      QsgsComponents.OLChoiceButton {
        id: endPhaseButton
        text: Lua.tr("End")
        visible: false;
        onClicked: Lua.call("UpdateRequestUI", "Button", "End");
        source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/selfArea/choice-button-end.png"
        glow.color: '#300d04'
      }
    } 
  }

  //selfArea 私人区域
  Item {
    id: selfArea
    width: roomScene.width
    height: 200
    anchors.bottom: roomScene.bottom

    property bool isGold: Logic.getGameTimeById(Self.id) > 3600 * 100

    Rectangle {
      id: dashboardBg
      anchors.left: dashboard.left
      anchors.right: dashboard.right
      anchors.bottom: dashboard.bottom
      anchors.top: selfEquipArea.top
      color: "transparent"
      z: -1

      Image {
        id: dashboardHandBg
        source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/selfArea/dashboard-bg" + (selfArea.isGold ? "-gold" : "") + ".png"
        anchors.fill: parent
      }

      Image {
        id: dashboardPattern
        source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/selfArea/dashboard-pattern-gold.png"
        anchors.fill: parent
        visible: selfArea.isGold
      }

      Image {
        id: dashboardMessageBg
        anchors.right: parent.right
        anchors.rightMargin: -145
        anchors.bottom: parent.bottom
        anchors.top: parent.top
        fillMode: Image.PreserveAspectFit
        source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/selfArea/dashboard-edging" + (selfArea.isGold ? "-gold" : "") + ".png"
      }
    }

    QsgsSelf.DelayedTrickArea {
      id: selfDelayTrickAreaItem
      anchors.bottom: parent.top
      anchors.left: parent.left
      anchors.leftMargin: 3
      anchors.bottomMargin: -107
      z: 1
    }

    Item {
      id: selfPicMarkArea
      width: childrenRect.width
      height: childrenRect.height
      anchors.top: selfEquipArea.top
      anchors.topMargin: 3
      anchors.right: dashboard.right
      anchors.rightMargin: 160
      z: 1

      QsgsSelf.PicMarkArea {
        id: selfPicMarkAreaItem
      }

      /* QsgsComponents.OLLimitSkillArea {
        id: selfLimitSkillAreaItem
        anchors.top: parent.top
        anchors.right: selfPicMarkAreaItem.left
      } */

    }

    Item {
      id: selfPhaseMark
      anchors.left: selfEquipArea.right
      anchors.leftMargin: 120
      anchors.bottom: selfPicMarkArea.top
      anchors.bottomMargin: 10
      height: childrenRect.height
      width: childrenRect.width
      visible: false

      property int selfPhase: 1

      Image {
        id: selfPhaseMarkImg
        width: sourceSize.width * 0.75
        height: sourceSize.height * 0.75
        property list<string> selfPhaseStr: ["round_start", "start", "judge", "draw", "play", "discard", "finish", "finish"]
        source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/selfArea/phase/" + selfPhaseStr[parent.selfPhase - 1] + ".png"
        visible: parent.selfPhase < 8

        onSourceChanged: {
          anim.stop()
          x = 15;
          anim.start()
        }

        NumberAnimation {
          id: anim
          target: selfPhaseMarkImg
          property: "x"
          to: 0
          duration: 300
          easing.type: Easing.OutCubic
        }
      }
    }

    Item {
      id: selfPhotoArea
      anchors.right: parent.right
      anchors.bottom: parent.bottom
      height: selfPhotoItem.height
      width: {
        // 给圆角屏幕留16px空隙
        let ret = selfPhotoItem.width + 16;
        if (selfDeputyPhotoItem.visible) {
          ret += selfDeputyPhotoItem.width; // + selfSpecialHp.width;
          ret += selfDeputyPhotoItem.anchors.leftMargin;
        }
        return ret;
      }

      QsgsComponents.DashboardPhoto {
        id: selfPhotoItem
        playerid: selfPhoto.id
        general: selfPhoto.general
        needHpBar: !selfPhoto.deputyGeneral
        avatar: selfPhoto.avatar
        screenName: selfPhoto.screenName
        role: selfPhoto.role
        role_shown: selfPhoto.role_shown
        role_visible: false
        kingdom: selfPhoto.kingdom
        netstate: selfPhoto.netstate
        maxHp: selfPhoto.maxHp
        hp: selfPhoto.hp
        shield: selfPhoto.shield
        seatNumber: selfPhoto.seatNumber
        dead: selfPhoto.dead
        dying: selfPhoto.dying
        faceup: selfPhoto.faceup
        chained: selfPhoto.chained
        drank: selfPhoto.drank
        rest: selfPhoto.rest
        surrendered: selfPhoto.surrendered
        sealedSlots: JSON.parse(selfPhoto.sealedSlots)
        frameStyle: Logic.getLevelById(selfPhoto.id)

        // anchors.right: selfDeputyPhotoItem.visible ? selfDeputyPhotoItem.left : parent.right
        // anchors.rightMargin: selfDeputyPhotoItem.visible ? -4 : 0
        anchors.bottom: parent.bottom

        onSelectedChanged: {
          if ( state === "candidate" )
            Lua.call("UpdateRequestUI", "Photo", playerid, "click", { selected, autoTarget: Config.autoTarget } );
        }

        Component.onCompleted: {
          dashboard.self = this;
        }
      }

      QsgsComponents.DashboardPhoto {
        id: selfDeputyPhotoItem
        playerid: selfPhoto.id
        general: selfPhoto.deputyGeneral
        isDeputy: true
        needHpBar: visible
        visible: !!selfPhoto.deputyGeneral
        avatar: selfPhoto.avatar
        screenName: selfPhoto.screenName
        role: selfPhoto.role
        role_shown: selfPhoto.role_shown
        role_visible: false
        kingdom: selfPhoto.kingdom
        netstate: selfPhoto.netstate
        maxHp: selfPhoto.maxHp
        hp: selfPhoto.hp
        shield: selfPhoto.shield
        dead: selfPhoto.dead
        dying: selfPhoto.dying
        faceup: selfPhoto.faceup
        chained: selfPhoto.chained
        drank: selfPhoto.drank
        rest: selfPhoto.rest
        surrendered: selfPhoto.surrendered
        sealedSlots: JSON.parse(selfPhoto.sealedSlots)
        frameStyle: Logic.getLevelById(selfPhoto.id)

        anchors.left: selfPhotoItem.right
        anchors.leftMargin: -3
        anchors.bottom: parent.bottom

        onSelectedChanged: {
          if ( state === "candidate" )
            Lua.call("UpdateRequestUI", "Photo", playerid, "click", { selected, autoTarget: Config.autoTarget } );
        }
      }

      /*
      QsgsSelf.HpBar {
        id: selfSpecialHp
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 2
        anchors.left: selfDeputyPhotoItem.right
        visible: selfDeputyPhotoItem.visible
        value: selfPhoto.hp
        maxValue: selfPhoto.maxHp
        shieldNum: selfPhoto.shield
      }
      */
    }

    //一点小智慧
    Image {
      id: dashboardMessageBgCopy
      anchors.right: selfPhotoArea.right
      anchors.rightMargin: -2
      anchors.bottom: dashboard.bottom
      anchors.top: dashboard.top
      fillMode: Image.PreserveAspectFit
      source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/selfArea/dashboard-edging" + (selfArea.isGold ? "-gold" : "") + ".png"
      sourceClipRect: Qt.rect(155, 0, 212 - 155, 149)
      z: -1
    }

    Dashboard {
      id: dashboard
      width: parent.width - selfEquipArea.width
      anchors.bottom: parent.bottom
      height: 132
      anchors.right: selfPhotoArea.left
      anchors.left: selfEquipArea.right
      self_role: selfPhoto.role
      self_seat: selfPhoto.seatNumber
    }

    Row {
      id: quickButtonArea
      anchors.bottom: selfEquipArea.top
      anchors.bottomMargin: 5
      anchors.left: dashboard.left
      anchors.leftMargin: 10

      height: childrenRect.height
      spacing: 1

      QsgsButton.SelectImageButton {
        id: trustButton
        text: Lua.tr("Trust")
        enabled: !Config.observing && !Config.replaying
        visible: !Config.observing && !Config.replaying
        height: 20
        onClicked: {
          Cpp.notifyServer("Trust", "");
          trustButton.enabled = false;
        }
      }
      QsgsButton.SelectImageButton {
        id: reverseButton
        text: Lua.tr("Revert Selection")
        height: 20
        enabled: dashboard.pending_skill !== ""

        onClicked: {
          Lua.call("RevertSelection");
        }
      }

      QsgsButton.SelectImageButton {
        id: sortButton
        text: Lua.tr("Sort Cards")
        height: 20
        enabled: dashboard.sortable

        onClicked: {
          if (dashboard.sortable) {
            let sortMethods = [];
            for (let index = 0; index < sortMenuRepeater.count; index++) {
              var tCheckBox = sortMenuRepeater.itemAt(index)
              sortMethods.push(tCheckBox.checked)
            }
            Logic.sortHandcards(sortMethods);
          }
        }

        onRightClicked: {
          if (sortMenu.visible) {
            sortMenu.close();
          } else {
            sortMenu.open();
          }
        }

        Menu {
          id: sortMenu
          x: parent.width
          y: -25
          width: parent.width + 100
          background: Rectangle {
            color: "black"
            border.width: 3
            border.color: "white"
            opacity: 0.8
          }

          Repeater {
            id: sortMenuRepeater
            model: ["Sort by Type", "Sort by Number", "Sort by Suit"]

            CheckBox {
              id: control
              width: 200
              text: "<font color='white'>" + Lua.tr(modelData) + "</font>"
              checked: modelData === "Sort by Type"
              font.pixelSize: 20

              indicator: Rectangle {
                implicitWidth: 26
                implicitHeight: 26
                x: control.leftPadding
                y: control.height / 2 - height / 2
                radius: 3
                border.color: "white"

                Rectangle {
                  width: 14
                  height: 14
                  x: 6
                  y: 6
                  radius: 2
                  color: control.down ? "#17a81a" : "#21be2b"
                  visible: control.checked
                }
              }
            }
          }
        }
      }

    }

    Item {
      id: selfEquipArea
      anchors.left: parent.left
      anchors.bottom: parent.bottom
      height: childrenRect.height
      width: childrenRect.width

      QsgsComponents.DashboardEquipArea {
        id: selfEquipAreaItem
        isGold: selfArea.isGold
      }
    }
  }

  //chatArea 聊天框
  Item {
    id: chatArea
    width: 220
    height: roomScene.height - selfPhotoArea.height
    z: -1
    anchors {
      right: roomScene.right
      top: roomScene.top
      // rightMargin: 10
      // topMargin: 40
    }

    // Rectangle {
    //   anchors.top: splitter.bottom
    //   anchors.bottom: bottomContent.bottom
    //   anchors.left: parent.left
    //   anchors.right: parent.right
    //   // radius: 8
    // }


    ColumnLayout {
      anchors.fill: parent
      spacing: 0

      // 状态栏
      Rectangle {
        Layout.fillWidth: true
        Layout.preferredHeight: 35
        color: "transparent"
      }

      Rectangle {
        id: topContent
        Layout.fillWidth: true
        Layout.preferredHeight: parent.height * 0.5
        width: parent.width
        color: "transparent"   

        Rectangle {
          id: topContentRect
          color: "#b2231c16"
          anchors.top: drawerBar.bottom
          anchors.bottom: parent.bottom
          anchors.left: parent.left
          anchors.right: parent.right
        }

        Image {
          source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/system/topBannerBg.png"
          anchors.top: parent.top
          anchors.topMargin: 25 - height
          anchors.left: parent.left
          anchors.leftMargin: -1
          anchors.right: parent.right
          fillMode: Image.PreserveAspectFit
        }

        // 顶部标签切换区域
        W.ViewSwitcher {
          id: drawerBar
          height: 25
          width: parent.width
          currentIndex: 0

          highlight: Image {
            source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/system/topBg.png"
          }
          

          model: ["牌局","房间"]

          delegate: Item {
            width: (drawerBarText.width * 1.2) * 2
            height: 25

            GlowText {
              id: drawerBarText
              text: modelData
              anchors.centerIn: parent
              font.pixelSize: 12
              font.letterSpacing: 3
              font.bold: true
              glow.color: "black"
              glow.spread: 0.75
              glow.radius: 2
              color: "#e4d5a0"
            }

            TapHandler {
              onTapped: {
                log.currentIndex = log.model.count - 1
                if (index < 2) drawerBar.currentIndex = index
              }
            }
          }

          onCurrentIndexChanged: {
            if (currentIndex == 1) {
              playerListModel.clear();
              const ps = Lua.call("GetPlayersAndObservers");
              ps.forEach(p => {
                playerListModel.append({
                  id: p.id,
                  screenName: p.name,
                  general: p.general ?? "",
                  deputyGeneral: p.deputy ?? "",
                  observing: p.observing,
                  netState: p.state,
                  avatar: p.avatar,
                });
              });
            }
          }

          Rectangle {
            anchors.top: parent.bottom
            anchors.left: parent.left
            anchors.right: parent.right
            height: 1
            color: "#555555"
          }
        }

        // 上半部分内容（牌局和房间）
        SwipeView {
          anchors.top: drawerBar.bottom
          width: parent.width
          height: parent.height - drawerBar.height
          interactive: false
          currentIndex: drawerBar.currentIndex
          clip: true

          Item {
            LogEdit {
              id: log
              clip: true
              anchors.fill: parent
              anchors.topMargin: 8
              anchors.leftMargin: 4
              anchors.bottomMargin: 8
              highlight: Rectangle { color: "#b2231c16"; radius: 5; opacity: 0.5 }

              Component.onCompleted: {
                for (var i = 0; i < log.children.length; i++) {
                  var child = log.children[i];
                  if (child instanceof Button) {
                    child.visible = false;
                    break;
                  }
                }
              }

              Image {
                anchors.right: log.right
                anchors.bottom: log.bottom
                anchors.rightMargin: 14
                width: 16
                height: 16
                source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/system/returnBottom.png"
                fillMode: Image.PreserveAspectFit
                z: 999
                visible: log.currentIndex !== log.model.count - 1
                
                MouseArea {
                  anchors.fill: parent
                  onClicked: {
                    log.currentIndex = log.model.count - 1
                  }
                }
              }
            }
          }

          ListView {
            id: playerList
            clip: true
            ScrollBar.vertical: ScrollBar {}
            model: ListModel {
              id: playerListModel
            }

            delegate: ItemDelegate {
              width: playerList.width
              height: playerListText.height + 2
              Text {
                id: playerListText
                text: {
                  let ret = screenName;
                  if (observing) {
                    ret = ' *旁观* ' + ret;
                  }
                  if (netState == 2) {
                    ret = '<font color="orange">*托管*</font> ' + ret;
                  } else if (netState == 3) {
                    ret = ' <font color="red">*逃跑*</font> ' + ret;
                  } else if (netState == 5) {
                    ret = ' <font color="orange">*人机*</font> ' + ret;
                  } else if (netState == 6) {
                    ret = ' <font color="white">*离线*</font> ' + ret;
                  }
                  return ret;
                }
                font.pixelSize: 10
                color: "yellow"
              }
            }
          }
        }
      }

      // 可拖拽的分割线
      Rectangle {
        id: splitter
        Layout.preferredHeight: 2
        Layout.fillWidth: true
        color: "transparent"
        opacity: 0.7
        z: 2

        // 上三角形
        Canvas {
          id: topTriangle
          anchors.horizontalCenter: parent.horizontalCenter
          anchors.bottom: parent.top
          anchors.bottomMargin: 2
          width: 12
          height: 4
          onPaint: {
            var ctx = getContext("2d");
            ctx.clearRect(0, 0, width, height);
            ctx.fillStyle = "#beb99b";
            ctx.beginPath();
            ctx.moveTo(0, height);
            ctx.lineTo(width, height);
            ctx.lineTo(width/2, 0);
            ctx.closePath();
            ctx.fill();
          }
        }

        // 下三角形
        Canvas {
          id: bottomTriangle
          anchors.horizontalCenter: parent.horizontalCenter
          anchors.top: parent.bottom
          anchors.topMargin: 2
          width: 12
          height: 4
          onPaint: {
            var ctx = getContext("2d");
            ctx.clearRect(0, 0, width, height);
            ctx.fillStyle = "#beb99b";
            ctx.beginPath();
            ctx.moveTo(0, 0);
            ctx.lineTo(width, 0);
            ctx.lineTo(width/2, height);
            ctx.closePath();
            ctx.fill();
          }
        }

        MouseArea {
          id: splitterMouseArea
          anchors.fill: parent
          cursorShape: Qt.SizeVerCursor
          hoverEnabled: true
          property real startGlobalY: 0
          property real startTopHeight: 0

          onPressed: {
            startGlobalY = mapToItem(null, mouseX, mouseY).y;
            startTopHeight = topContent.Layout.preferredHeight;
          }

          onPositionChanged: {
            if (pressed) {
              // 使用全局坐标计算移动距离
              var currentGlobalY = mapToItem(null, mouseX, mouseY).y;
              var deltaY = currentGlobalY - startGlobalY;

              // 计算新的顶部高度
              var newTopHeight = startTopHeight + deltaY;

              // 限制高度在合理范围内
              var minHeight = 50;
              var maxHeight = chatArea.height - 150; // 为底部区域留出空间
              newTopHeight = Math.max(minHeight, Math.min(maxHeight, newTopHeight));

              // 应用新的高度
              topContent.Layout.preferredHeight = newTopHeight;

              // 更新起始位置，避免累积误差
              startGlobalY = currentGlobalY;
              startTopHeight = newTopHeight;

              log.currentIndex = log.currentIndex - 1
              log.currentIndex = log.currentIndex + 1
            }
          }

          onEntered: {
            parent.opacity = 1.0;
          }

          onExited: {
            if (!pressed) {
              parent.opacity = 0.7;
            }
          }
        }
      }

      // 下半部分聊天区域
      Rectangle {
        id: bottomContent
        Layout.fillWidth: true
        Layout.fillHeight: true
        visible: !Config.replaying
        color: "#cc1a1612"

        QsgsComponents.CustomChatBox {
          id: chat
          anchors.fill: parent
          Behavior on height { NumberAnimation { duration: 200 } }
          clip: false
        }

      }
    }
  }

  // manualBox: same as popupBox, but must be closed manually
  Loader {
    id: manualBox
    z: 999
    onSourceChanged: {
      if (item === null)
        return;
      item.finished.connect(() => sourceComponent = undefined);
      item.widthChanged.connect(() => manualBox.moveToCenter());
      item.heightChanged.connect(() => manualBox.moveToCenter());
      moveToCenter();
    }
    onSourceComponentChanged: sourceChanged();

    function moveToCenter() {
      item.x = Math.round((roomArea.width - item.width) / 2);
      item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
    }
  }

  Loader {
    id: popupBox
    z: 999
    onSourceChanged: {
      if (item === null)
        return;
      item.finished.connect(() => {
        sourceComponent = undefined;
      });
      item.widthChanged.connect(() => {
        popupBox.moveToCenter();
      });
      item.heightChanged.connect(() => {
        popupBox.moveToCenter();
      });
      moveToCenter();
    }
    onSourceComponentChanged: sourceChanged();

    function moveToCenter() {
      if (item.box_type === "choice_box") {
        item.x = Math.round((roomArea.width - item.width) / 2);
        item.y = mapFromItem(controls.progressBar, controls.progressBar.x, controls.progressBar.y).y + controls.progressBar.height + 15;
      } else {
        item.x = Math.round((roomArea.width - item.width) / 2);
        item.y = Math.round(roomArea.height * 0.67 - item.height / 2);
      }
    }
  }

  Loader {
    id: bigAnim
    anchors.fill: parent
    z: 999
  }

  Column {
    id: popupLogArea
    anchors.fill: roomArea
    anchors.topMargin: roomArea.height * 0.42
    spacing: 1
  }

  function activateSkill(skill_name, selected, action) {
    let data;
    if (action === "click") data = { selected, autoTarget: Config.autoTarget };
    else if (action === "doubleClick") data = { selected, doubleClickUse: Config.doubleClickUse, autoTarget: Config.autoTarget };
    else data = { selected };
    Lua.call("UpdateRequestUI", "SkillButton", skill_name, action, data);
  }

  W.PopupLoader {
    id: cheatLoader
    width: Config.winWidth * 0.60
    height: Config.winHeight * 0.8
    anchors.centerIn: parent
    background: Rectangle {
      color: "#CC2E2C27"
      radius: 5
      border.color: "#A6967A"
      border.width: 1
    }
  }

  Item {
    id: dynamicCardArea
    anchors.fill: parent
  }

  GlowText {
    anchors.centerIn: selfArea
    visible: Logic.getPhoto(Self.id).rest > 0 && !Config.observing
    text: Lua.tr("Resting, don't leave!")
    color: "#DBCC69"
    font.family: Config.libianName
    font.pixelSize: 28
    glow.color: "#2E200F"
    glow.spread: 0.6
  }

  Rectangle {
    anchors.fill: selfArea
    visible: Config.observing && !Config.replaying
    color: "transparent"
    Image {
      source: Cpp.path + "/packages/lunarltk-qsgs-ui/image/selfArea/observing.png"
      anchors.top: parent.top
      anchors.topMargin: 70
      anchors.left: parent.left
      anchors.leftMargin: 150
      height: 30
      fillMode: Image.PreserveAspectFit
    }
  }

  QsgsComponents.MiscStatus {
    id: miscStatus
    anchors.right: roomArea.right
    anchors.top: roomArea.top
    anchors.rightMargin: 10
    anchors.topMargin: 8
  }

  QsgsRoom.BannerMarkArea {
    id: banner
    width: ((roomScene.width - 175 * 0.75 * 7) / 4 + 175 - 16) * 0.75
    transformOrigin: Item.TopLeft
    bgColor: "#BB838AEA"
  }

  Danmu {
    id: danmu
    width: parent.width
  }

  Shortcut {
    sequence: "D"
    property bool show_distance: false
    onActivated: {
      show_distance = !show_distance;
      showDistance(show_distance);
    }
  }

  Shortcut {
    sequence: "Return"
    enabled: okButton.enabled
    onActivated: Lua.call("UpdateRequestUI", "Button", "OK");
  }

  Shortcut {
    sequence: "Space"
    enabled: cancelButton.enabled || endPhaseButton.visible;
    onActivated: if (cancelButton.enabled) {
      Lua.call("UpdateRequestUI", "Button", "Cancel");
    } else {
      Logic.replyToServer("");
    }
  }

  Timer {
    id: statusSkillTimer
    interval: 200
    running: true
    repeat: true
    onTriggered: {
      Lua.call("RefreshStatusSkills");
      // FIXME 本来可以用客户端notifyUI(AddObserver)刷旁观列表的
      // FIXME 但是由于重启智慧所以还是加入一秒0.2刷得了

      // 刷托管按钮
      trustButton.enabled = true;
      // 刷大家的明置手牌提示框
      for (let i = 0; i < photos.count; i++)
        photos.itemAt(i).handcardsChanged();
    }
  }

  function showDistance(show) {
    for (let i = 0; i < photoModel.count; i++) {
      const item = photos.itemAt(i);
      if (show) {
        item.distance = Lua.call("DistanceTo", Self.id, item.playerid);
      } else {
        item.distance = -1;
      }
    }
  }

  function startCheat(type, data) {
    let component = Qt.createComponent(type);
    if (component.status !== Component.Ready) {
      component = Qt.createComponent("Fk.Components.LunarLTK.Cheat", type);
    }
    cheatLoader.sourceComponent = component;
    cheatLoader.item.extra_data = data;
    cheatLoader.open();
  }

  function startCheatByPath(path, data) {
    cheatLoader.sourceComponent = Qt.createComponent(`${Cpp.path}/${path}.qml`);
    cheatLoader.item.extra_data = data;
    cheatLoader.open();
  }

  function closeCheat() {
    cheatLoader.close();
  }

  function setPrompt(text, iscur) {
    promptText = text;
    if (iscur) currentPrompt = text;
  }

  function resetPrompt() {
    promptText = currentPrompt;
  }

  function getPhoto(id) {
    return Logic.getPhoto(id);
  }

  function getPhotoOrDashboard(id) {
    if (id === Self.id) return dashboard;
    return getPhoto(id);
  }

  function activate() {
    if (state === "active") state = "notactive";
    state = "active";
  }

  function applyChange(uiUpdate) {
    const sskilldata = uiUpdate["SpecialSkills"]?.[0]
    if (sskilldata) {
      specialCardSkills.model = sskilldata?.skills ?? [];
    }
    selfEquipAreaItem.equipArea.applyChange(uiUpdate);
    dashboard.applyChange(uiUpdate);
    const pdatas = uiUpdate["Photo"];
    pdatas?.forEach(pdata => {
      const photo = Logic.getPhoto(pdata.id);
      photo.state = pdata.state;
      photo.selectable = pdata.enabled;
      photo.selected = pdata.selected;
      if (pdata.id === Self.id) {
        selfDeputyPhotoItem.state = pdata.state;
        selfDeputyPhotoItem.selectable = pdata.enabled;
        selfDeputyPhotoItem.selected = pdata.selected;
      }
    });
    for (let i = 0; i < photoModel.count; i++) {
      const item = photos.itemAt(i);
      item.targetTip = Lua.call("GetTargetTip", item.playerid);
    }
    selfPhotoItem.targetTip = Lua.call("GetTargetTip", Self.id);

    const buttons = uiUpdate["Button"];
    if (buttons) {
      okCancel.visible = true;
    }
    buttons?.forEach(bdata => {
      switch (bdata.id) {
        case "OK":
          okButton.enabled = bdata.enabled;
          break;
        case "Cancel":
          cancelButton.enabled = bdata.enabled;
          break;
        case "End":
          endPhaseButton.enabled = bdata.enabled;
          endPhaseButton.visible = bdata.enabled;
          break;
      }
    })

    // Interaction最后上桌 太给脸了居然插结
    uiUpdate["_delete"]?.forEach(data => {
      if (data.type == "Interaction") {
        skillInteraction.sourceComponent = undefined;
        if (roomScene.popupBox.item)
          roomScene.popupBox.item.close();
      }
    });
    uiUpdate["_new"]?.forEach(dat => {
      if (dat.type == "Interaction") {
        const data = dat.data.spec;
        const skill_name = dat.data.skill_name;
        switch (data.type) {
        case "combo":
          skillInteraction.sourceComponent =
            Qt.createComponent("components/skillInteraction/SkillCombo.qml");
          skillInteraction.item.skill = skill_name;
          skillInteraction.item.default_choice = data["default"];
          skillInteraction.item.choices = data.choices;
          skillInteraction.item.detailed = data.detailed;
          skillInteraction.item.all_choices = data.all_choices;
          skillInteraction.item.clicked();
          break;
        case "spin":
          skillInteraction.sourceComponent =
            Qt.createComponent("components/skillInteraction/SkillSpin.qml");
          skillInteraction.item.skill = skill_name;
          skillInteraction.item.from = data.from;
          skillInteraction.item.to = data.to;
          skillInteraction.item?.clicked();
          break;
        case "custom":
          skillInteraction.sourceComponent =
            Qt.createComponent(Cpp.path + "/" + data.qml_path + ".qml");
          skillInteraction.item.skill = skill_name;
          skillInteraction.item.extra_data = data;
          skillInteraction.item?.clicked();
          break;
        case "cardname":
          skillInteraction.sourceComponent =
            Qt.createComponent("components/skillInteraction/SkillCardName.qml");
          skillInteraction.item.skill = skill_name;
          skillInteraction.item.extra_data = data;
          skillInteraction.item?.clicked();
          break;
        case "checkbox":
          skillInteraction.sourceComponent =
            Qt.createComponent("components/skillInteraction/SkillCheckBox.qml");
          skillInteraction.item.skill = skill_name;
          skillInteraction.item.choices = data.choices;
          skillInteraction.item.detailed = data.detailed;
          skillInteraction.item.all_choices = data.all_choices;
          skillInteraction.item.min_num = data.min_num;
          skillInteraction.item.max_num = data.max_num;
          skillInteraction.item.cancelable = data.cancelable;
          skillInteraction.item.clicked();
          break;
        default:
          skillInteraction.sourceComponent = undefined;
          break;
        }
      }
    });
  }

  function netStateChanged(sender, data) {
    const id = data[0];
    let state = data[1];

    const model = Logic.getPhotoModel(id);
    if (!model) return;
    if (state === "run" && model.dead) {
      state = "leave";
    };

    if (id < 0) {
      state = "robot";
    }
    model.netstate = state;
    if (state === "trust" && id === Self.id) {
      roomScene.state = "notactive";
    }
  }

  function processLogText(text) {
    var colorMap = {
      "black": "grey",
      "#0598BC": '#ee9b00',
      "#0C8F0C": '#fbff00',
      "#CC3132": '#fbff00',
      "#CC3131": '#CC3131',
      "#b5ba00": '#60ff51',
      "grey": "#a5a5a5",
    };

    var processedText = text;

    processedText = processedText.replace(/<b>/gi, "");
    processedText = processedText.replace(/<\/b>/gi, "");
    processedText = processedText.replace(/<strong>/gi, "");
    processedText = processedText.replace(/<\/strong>/gi, "");
    
    processedText = processedText.replace(/style\s*=\s*['"][^'"]*font-weight\s*:\s*bold[^'"]*['"]/gi, "");
    processedText = processedText.replace(/font-weight\s*:\s*bold/gi, "");

    for (var colorName in colorMap) {
      // 匹配 color='black' 或 color="black" 或 color=black
      var regex1 = new RegExp("color\\s*=\\s*['\"]" + colorName + "['\"]", "gi");
      var regex2 = new RegExp("color\\s*=\\s*" + colorName + "(?=[\\s>])", "gi");
      
      processedText = processedText.replace(regex1, "color='" + colorMap[colorName] + "'");
      processedText = processedText.replace(regex2, "color='" + colorMap[colorName] + "'");
    }

    processedText = processedText.replace(/♠/g, "<font color='#a5a5a5'>♠</font>");
    processedText = processedText.replace(/♣/g, "<font color='#a5a5a5'>♣</font>");

    //这个有点品味
    const boardStr = /发动了/
    if (boardStr.test(processedText)) {
      boardToRoomArea(processedText)
    }
    
    return '<font color="#e4d5a0"; size = "1">' + processedText + '</font>';
  }

  function addToLog(_, msg) {
    log.append({ logText: processLogText(msg) });
  }

  function boardToRoomArea(msg) {
    const component = Qt.createComponent(Cpp.path + "/packages/lunarltk-qsgs-ui/qml/lunarltk/components/PopupLog.qml");
    const popupLog = component.createObject(popupLogArea, {
      text: msg
    });
    popupLog.anchors.horizontalCenter = popupLogArea.horizontalCenter;
    popupLog.destroy(2300);
    popupLog.anim.start();
  }

  function addToChat(pid, raw) {
    if (raw.type === 1) return;
    const room = roomScene;

    const photo = Logic.getPhoto(pid);
    const general = Lua.tr(raw.general);
    if (!photo && Config.hideObserverChatter)
      return;

    if (raw.msg.startsWith("$@")) return;
    let text;
    if (general === "") {
      text = `<font color="#ffff51">[${raw.time}] ${raw.userName}:</font> ${raw.msg}`;
    }
    else {
      text = `<font color="#ffff51">[${raw.time}] ${raw.userName}` +
      `(${general}):</font> ${raw.msg}`;
    }
    text = text.replace(/\{emoji([0-9]+)\}/g,
    `<img src="${Cpp.path}/image/emoji/$1.png" height="13" width="13" />`);
    chat.append(processLogText(text), raw);
  }

  function chatToLog(sender, data) {
    // jsonData: { string userName, string general, string time, string msg }
    const pid = data.sender;
    const userName = data.userName;
    const general = Lua.tr(data.general);
    const time = data.time;
    const msg = data.msg;

    if (Config.blockedUsers.indexOf(userName) !== -1) {
      return;
    }

    addToChat(pid, data);
  }

  function updateMarkArea(sender, data) {
    const { id, change } = data;
    const photo = Logic.getPhoto(id);
    if (!photo) return;
    photo.handleMarkAreaUpdate(change);
  }

  Component.onCompleted: {
    addCallback(Command.NetStateChanged, netStateChanged);

    // TODO 摆烂了 反正这些后面也是得重构 懒得搬砖了
    addCallback(Command.SetCardFootnote,  Logic.SetCardFootnote);
    addCallback(Command.SetCardVirtName,  Logic.SetCardVirtName);
    addCallback(Command.ShowVirtualCard,  Logic.ShowVirtualCard);
    addCallback(Command.DestroyTableCard,  Logic.DestroyTableCard);
    addCallback(Command.DestroyTableCardByEvent,  Logic.DestroyTableCardByEvent);
    addCallback(Command.MaxCard,  Logic.MaxCard);
    addCallback(Command.PropertyUpdate,  Logic.PropertyUpdate);
    addCallback(Command.UpdateHandcard,  Logic.UpdateHandcard);
    addCallback(Command.UpdateCard,  Logic.UpdateCard);
    addCallback(Command.UpdateSkill,  Logic.UpdateSkill);
    addCallback(Command.StartGame,  Logic.StartGame);
    addCallback(Command.ArrangeSeats,  Logic.ArrangeSeats);
    addCallback(Command.MoveFocus,  Logic.MoveFocus);
    addCallback(Command.PlayerRunned,  Logic.PlayerRunned);
    addCallback(Command.AskForGeneral,  Logic.AskForGeneral);
    addCallback(Command.AskForSkillInvoke,  Logic.AskForSkillInvoke);
    addCallback(Command.AskForArrangeCards,  Logic.AskForArrangeCards);
    addCallback(Command.AskForGuanxing,  Logic.AskForGuanxing);
    addCallback(Command.AskForExchange,  Logic.AskForExchange);
    addCallback(Command.AskForChoice,  Logic.AskForChoice);
    addCallback(Command.AskForChoices,  Logic.AskForChoices);
    addCallback(Command.AskForCardChosen,  Logic.AskForCardChosen);
    addCallback(Command.AskForCardsChosen,  Logic.AskForCardsChosen);
    addCallback(Command.AskForPoxi,  Logic.AskForPoxi);
    addCallback(Command.AskForMoveCardInBoard,  Logic.AskForMoveCardInBoard);
    addCallback(Command.AskForCardsAndChoice,  Logic.AskForCardsAndChoice);
    addCallback(Command.MoveCards,  Logic.DoMoveCards);
    addCallback(Command.PlayCard,  Logic.PlayCard);
    addCallback(Command.LoseSkill,  Logic.LoseSkill);
    addCallback(Command.AddSkill,  Logic.AddSkill);
    addCallback(Command.PrelightSkill,  Logic.PrelightSkill);
    addCallback(Command.AskForUseActiveSkill,  Logic.AskForUseActiveSkill);
    addCallback(Command.CancelRequest,  Logic.CancelRequest);
    addCallback(Command.AskForUseCard,  Logic.AskForUseCard);
    addCallback(Command.AskForResponseCard,  Logic.AskForResponseCard);
    addCallback(Command.SetPlayerMark,  Logic.SetPlayerMark);
    addCallback(Command.SetBanner,  Logic.SetBanner);
    addCallback(Command.Animate,  Logic.Animate);
    addCallback(Command.LogEvent,  Logic.LogEvent);
    addCallback(Command.GameOver,  Logic.GameOver);
    addCallback(Command.FillAG,  Logic.FillAG);
    addCallback(Command.AskForAG,  Logic.AskForAG);
    addCallback(Command.TakeAG,  Logic.TakeAG);
    addCallback(Command.CloseAG,  Logic.CloseAG);
    addCallback(Command.CustomDialog,  Logic.CustomDialog);
    addCallback(Command.MiniGame,  Logic.MiniGame);
    addCallback(Command.UpdateMiniGame,  Logic.UpdateMiniGame);
    addCallback(Command.EmptyRequest,  Logic.EmptyRequest);
    addCallback(Command.UpdateLimitSkill,  Logic.UpdateLimitSkill);
    addCallback(Command.UpdateDrawPile,  Logic.UpdateDrawPile);
    addCallback(Command.UpdateRoundNum,  Logic.UpdateRoundNum);
    addCallback(Command.ChangeSelf,  Logic.ChangeSelf);
    addCallback(Command.UpdateRequestUI,  Logic.UpdateRequestUI);
    addCallback(Command.GetPlayerHandcards,  Logic.GetPlayerHandcards);
    addCallback(Command.ReplyToServer,  Logic.ReplyToServer);
    addCallback(Command.ChangeSkin, Logic.changeSkin);

    addCallback(Command.GameLog, addToLog);
    addCallback(Command.Chat, chatToLog);

    addCallback(Command.UpdateMarkArea, updateMarkArea);

    playerNum = Config.roomCapacity;
    bgm.play();

    const luaSelfIdx = Lua.evaluate('table.indexOf(ClientInstance.players, Self)') - 1;

    selfPhoto.id = Self.id;
    dashboardId = Self.id;
    for (let i = 1; i < playerNum; i++) {
      const state = Lua.evaluate(`ClientInstance.players[${(luaSelfIdx + i) % playerNum + 1}]:__toqml().prop`);
      const modelData = {
        id: -1,
        index: i,   // For animating seat swap
        general: "anjiang",
        avatar: "",
        deputyGeneral: "",
        screenName: "",
        role: "unknown",
        role_shown: false,
        kingdom: "unknown",
        netstate: "online",
        maxHp: 0,
        hp: 0,
        shield: 0,
        seatNumber: i + 1,
        dead: false,
        dying: false,
        faceup: true,
        chained: false,
        drank: 0,
        rest: 0,
        surrendered: false,
        sealedSlots: "[]",
      };
      Object.assign(modelData, state);
      modelData.id = state.playerid;
      modelData.netstate = state.playerid > 0 ? "online" : "robot";

      photoModel.append(modelData);
    }

    Logic.arrangePhotos();
  }
}
